﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Fiddler;
using System.Security.Cryptography;
using System.Windows.Controls.Primitives;
using System.ComponentModel;
using System.Windows.Threading;
using System.Threading.Tasks;
using System.Net;
using System.Xml.Linq;
using System.Diagnostics;
using System.Text.RegularExpressions;

namespace JsonEditor
{
	[TemplatePart(Name = "PART_ResizePropertyEditorThumb", Type = typeof(Thumb))]
	[TemplatePart(Name = "PART_SearchResultThumb", Type = typeof(Thumb))]
	[TemplatePart(Name = "PART_DragThumb", Type = typeof(Thumb))]
	[TemplatePart(Name = "PART_DragTranslateTransform", Type = typeof(TranslateTransform))]
	[TemplatePart(Name = "PART_PropertyEditor", Type = typeof(Border))]
	public partial class JsonEditorControl : UserControl
	{
		public JsonEditorControl()
		{
			InitializeComponent();

			this.EditPropertyCommand = new RoutedUICommand();
			this.CommandBindings.Add(new CommandBinding(EditPropertyCommand, EditProperty_Executed, EditProperty_CanExecute));

			this.EditValueCommand = new RoutedUICommand();
			this.CommandBindings.Add(new CommandBinding(EditValueCommand, EditValue_Executed, EditValue_CanExecute));

			this.RemoveCommand = new RoutedUICommand();
			this.CommandBindings.Add(new CommandBinding(RemoveCommand, Remove_Executed, Remove_CanExecute));

			this.RemoveAllCommand = new RoutedUICommand();
			this.CommandBindings.Add(new CommandBinding(RemoveAllCommand, RemoveAll_Executed, RemoveAll_CanExecute));

			this.CopyCommand = new RoutedUICommand();
			this.CommandBindings.Add(new CommandBinding(CopyCommand, Copy_Executed, Copy_CanExecute));

			this.PasteCommand = new RoutedUICommand();
			this.CommandBindings.Add(new CommandBinding(PasteCommand, Paste_Executed, Paste_CanExecute));

			this.OkCommand = new RoutedUICommand();
			this.CommandBindings.Add(new CommandBinding(OkCommand, Ok_Executed, Ok_CanExecute));

			this.CancelCommand = new RoutedUICommand();
			this.CommandBindings.Add(new CommandBinding(CancelCommand, Cancel_Executed, Cancel_CanExecute));

			this.NewPropertyCommand = new RoutedUICommand();
			this.CommandBindings.Add(new CommandBinding(NewPropertyCommand, NewProperty_Executed, NewProperty_CanExecute));

			this.NewObjectCommand = new RoutedUICommand();
			this.CommandBindings.Add(new CommandBinding(NewObjectCommand, NewObject_Executed, NewObject_CanExecute));

			this.NewValueCommand = new RoutedUICommand();
			this.CommandBindings.Add(new CommandBinding(NewValueCommand, NewValue_Executed, NewValue_CanExecute));

			this.NavigateToCommand = new RoutedUICommand();
			this.CommandBindings.Add(new CommandBinding(NavigateToCommand, NavigateTo_Executed, NavigateTo_CanExecute));

			this.SearchCommand = new RoutedUICommand();
			this.CommandBindings.Add(new CommandBinding(SearchCommand, Search_Executed, Search_CanExecute));

			this.ToggleSearchResultsCommand = new RoutedUICommand();
			this.CommandBindings.Add(new CommandBinding(ToggleSearchResultsCommand, ToggleSearchResults_Executed, ToggleSearchResults_CanExecute));

			this.ViewUpdateCommand = new RoutedUICommand();
			this.CommandBindings.Add(new CommandBinding(ViewUpdateCommand, ViewUpdate_Executed, ViewUpdate_CanExecute));

			CheckUpdateAvailable();
		}

		#region "       Properties      "

		private TranslateTransform DragTranslateTransform { get; set; }

		private Border PropertyEditor { get; set; }

		private Thumb DragThumb { get; set; }

		private Thumb SearchResultThumb { get; set; }

		private Thumb ResizePropertyEditorThumb { get; set; }

		public byte[] OriginalBody { get; set; }

		public byte[] Body
		{
			get
			{
				return ToByteArray(this.DataContext as JToken);
			}
			set
			{
				TreeViewItem rootItem = this.RootTreeview.Items[0] as TreeViewItem;

				if (rootItem != null)
					rootItem.IsSelected = false;

				this.DataContext = this.ToJToken(value);

				if (rootItem != null)
					rootItem.IsSelected = true;

				this.FoundItems = null;
				this.SearchResultsVisbility = Visibility.Collapsed;

				UnsetDirtyFlag();
			}
		}

		#region "       EditedJToken      "

		public EditedJToken EditedJToken
		{
			get { return (EditedJToken)GetValue(EditedJTokenProperty); }
			set { SetValue(EditedJTokenProperty, value); }
		}

		public static readonly DependencyProperty EditedJTokenProperty = DependencyProperty.Register("EditedJToken", typeof(EditedJToken), typeof(JsonEditorControl), new UIPropertyMetadata(null));

		#endregion

		#region "       IsReadOnly      "

		public bool IsReadOnly
		{
			get { return (bool)GetValue(IsReadOnlyProperty); }
			set { SetValue(IsReadOnlyProperty, value); }
		}

		public static readonly DependencyProperty IsReadOnlyProperty = DependencyProperty.Register("IsReadOnly", typeof(bool), typeof(JsonEditorControl), new UIPropertyMetadata(false));

		#endregion

		#region "       IsDirty      "

		public bool IsDirty
		{
			get
			{
				if (this.DataContext != null)
				{
					byte[] body = ToByteArray(this.DataContext as JToken);

					return !this.OriginalBody.SequenceEqual(body);
				}
				else
					return false;
			}
		}

		#endregion

		#region "       EditValueVisibility     "

		public Visibility EditValueVisibility
		{
			get { return (Visibility)GetValue(EditValueVisibilityProperty); }
			set { SetValue(EditValueVisibilityProperty, value); }
		}

		public static readonly DependencyProperty EditValueVisibilityProperty = DependencyProperty.Register("EditValueVisibility", typeof(Visibility), typeof(JsonEditorControl), new UIPropertyMetadata(Visibility.Collapsed));

		#endregion

		#region "       SearchResultsVisbility      "

		public Visibility SearchResultsVisbility
		{
			get { return (Visibility)GetValue(SearchResultsVisbilityProperty); }
			set { SetValue(SearchResultsVisbilityProperty, value); }
		}

		public static readonly DependencyProperty SearchResultsVisbilityProperty =
			DependencyProperty.Register("SearchResultsVisbility", typeof(Visibility), typeof(JsonEditorControl), new UIPropertyMetadata(Visibility.Collapsed));

		#endregion

		#region "       IsSearching     "

		public bool IsSearching
		{
			get { return (bool)GetValue(IsSearchingProperty); }
			set { SetValue(IsSearchingProperty, value); }
		}

		public static readonly DependencyProperty IsSearchingProperty =
			DependencyProperty.Register("IsSearching", typeof(bool), typeof(JsonEditorControl), new UIPropertyMetadata(false));

		#endregion

		#region "       FoundItems      "

		public IEnumerable<JToken> FoundItems
		{
			get { return (IEnumerable<JToken>)GetValue(FoundItemsProperty); }
			set { SetValue(FoundItemsProperty, value); }
		}

		public static readonly DependencyProperty FoundItemsProperty =
			DependencyProperty.Register("FoundItems", typeof(IEnumerable<JToken>), typeof(JsonEditorControl), new UIPropertyMetadata(null));

		#endregion

		#region "       IsUpdateAvailable       "

		public bool IsUpdateAvailable
		{
			get { return (bool)GetValue(IsUpdateAvailableProperty); }
			set { SetValue(IsUpdateAvailableProperty, value); }
		}

		public static readonly DependencyProperty IsUpdateAvailableProperty = DependencyProperty.Register("IsUpdateAvailable", typeof(bool), typeof(JsonEditorControl), new UIPropertyMetadata(false));

		#endregion

		#region "       SearchResultHeight      "

		public double SearchResultHeight
		{
			get { return (double)GetValue(SearchResultHeightProperty); }
			set { SetValue(SearchResultHeightProperty, value); }
		}

		public static readonly DependencyProperty SearchResultHeightProperty =
			DependencyProperty.Register("SearchResultHeight", typeof(double), typeof(JsonEditorControl), new UIPropertyMetadata(150d));

		#endregion

		#endregion

		#region "       Commands        "

		#region "       EditPropertyCommand     "

		public RoutedUICommand EditPropertyCommand
		{
			get { return (RoutedUICommand)GetValue(EditPropertyCommandProperty); }
			set { SetValue(EditPropertyCommandProperty, value); }
		}

		public static readonly DependencyProperty EditPropertyCommandProperty = DependencyProperty.Register("EditPropertyCommand", typeof(RoutedUICommand), typeof(JsonEditorControl), new PropertyMetadata(null));

		private void EditProperty_Executed(object sender, ExecutedRoutedEventArgs e)
		{
			JProperty jProperty = e.Parameter as JProperty;

			this.EditToken(jProperty);
		}

		private void EditProperty_CanExecute(object sender, CanExecuteRoutedEventArgs e)
		{
			e.CanExecute = !this.IsReadOnly;
		}

		#endregion

		#region "       EditValueCommand     "

		public RoutedUICommand EditValueCommand
		{
			get { return (RoutedUICommand)GetValue(EditValueCommandProperty); }
			set { SetValue(EditValueCommandProperty, value); }
		}

		public static readonly DependencyProperty EditValueCommandProperty = DependencyProperty.Register("EditValueCommand", typeof(RoutedUICommand), typeof(JsonEditorControl), new PropertyMetadata(null));

		private void EditValue_Executed(object sender, ExecutedRoutedEventArgs e)
		{
			JValue jValue = e.Parameter as JValue;

			this.EditToken(jValue);
		}

		private void EditValue_CanExecute(object sender, CanExecuteRoutedEventArgs e)
		{
			e.CanExecute = !this.IsReadOnly;
		}

		#endregion

		#region "       RemoveCommand     "

		public RoutedUICommand RemoveCommand
		{
			get { return (RoutedUICommand)GetValue(RemoveCommandProperty); }
			set { SetValue(RemoveCommandProperty, value); }
		}

		public static readonly DependencyProperty RemoveCommandProperty = DependencyProperty.Register("RemoveCommand", typeof(RoutedUICommand), typeof(JsonEditorControl), new PropertyMetadata(null));

		private void Remove_Executed(object sender, ExecutedRoutedEventArgs e)
		{
			JToken jToken = e.Parameter as JToken;
			JToken parent = null;

			if (jToken != null)
			{
				parent = jToken.Parent;

				jToken.Remove();

				//Force UI refresh on properties with the arrays so counts 
				//  and the object indexes are updated
				if (parent != null && parent is JArray)
					this.RefreshTreeViewItem(parent);
			}
		}

		private void Remove_CanExecute(object sender, CanExecuteRoutedEventArgs e)
		{
			JToken jToken = e.Parameter as JToken;

			if (jToken != null && jToken.Parent != null)
				e.CanExecute = !this.IsReadOnly;
		}

		#endregion

		#region "       RemoveAllCommand     "

		public RoutedUICommand RemoveAllCommand
		{
			get { return (RoutedUICommand)GetValue(RemoveAllCommandProperty); }
			set { SetValue(RemoveAllCommandProperty, value); }
		}

		public static readonly DependencyProperty RemoveAllCommandProperty = DependencyProperty.Register("RemoveAllCommand", typeof(RoutedUICommand), typeof(JsonEditorControl), new PropertyMetadata(null));

		private void RemoveAll_Executed(object sender, ExecutedRoutedEventArgs e)
		{
			JProperty jProperty = e.Parameter as JProperty;
			JContainer jContainer = null;

			if (jProperty != null)
				jContainer = jProperty.Value as JContainer;
			else
				jContainer = e.Parameter as JContainer;

			if (jContainer != null)
				jContainer.RemoveAll();

			//Force UI refresh on properties with the arrays so counts are updated
			if (jProperty != null && jProperty.Value is JArray)
				this.RefreshTreeViewItem(jProperty);
		}

		private void RemoveAll_CanExecute(object sender, CanExecuteRoutedEventArgs e)
		{
			JProperty jProperty = e.Parameter as JProperty;
			JContainer jContainer = null;

			if (jProperty != null)
				jContainer = jProperty.Value as JContainer;
			else
				jContainer = e.Parameter as JContainer;

			if (jContainer != null)
				e.CanExecute = !this.IsReadOnly && jContainer.Count != 0;
		}

		#endregion

		#region "       CopyCommand     "

		public RoutedUICommand CopyCommand
		{
			get { return (RoutedUICommand)GetValue(CopyCommandProperty); }
			set { SetValue(CopyCommandProperty, value); }
		}

		public static readonly DependencyProperty CopyCommandProperty = DependencyProperty.Register("CopyCommand", typeof(RoutedUICommand), typeof(JsonEditorControl), new PropertyMetadata(null));

		private void Copy_Executed(object sender, ExecutedRoutedEventArgs e)
		{
			JToken jToken = e.Parameter as JToken;

			if (jToken == null)
				Clipboard.SetText(e.Parameter.ToString());
			else
				Clipboard.SetText(jToken.ToString(Formatting.Indented));
		}

		private void Copy_CanExecute(object sender, CanExecuteRoutedEventArgs e)
		{
			e.CanExecute = true;
		}

		#endregion

		#region "       PasteCommand     "

		public RoutedUICommand PasteCommand
		{
			get { return (RoutedUICommand)GetValue(PasteCommandProperty); }
			set { SetValue(PasteCommandProperty, value); }
		}

		public static readonly DependencyProperty PasteCommandProperty = DependencyProperty.Register("PasteCommand", typeof(RoutedUICommand), typeof(JsonEditorControl), new PropertyMetadata(null));

		private void Paste_Executed(object sender, ExecutedRoutedEventArgs e)
		{
			JToken targetJToken = e.Parameter as JToken;
			JToken pastedJToken = GetJTokenFromClipBoard();
			JContainer jContainer = null;

			if (targetJToken is JProperty)
				jContainer = ((JProperty)targetJToken).Value as JContainer;
			else if (targetJToken is JContainer)
				jContainer = targetJToken as JContainer;

			if (jContainer != null)
			{
				if (jContainer is JObject && (pastedJToken is JObject || pastedJToken is JArray || pastedJToken is JValue))
					jContainer.Add(new JProperty(this.GetNewPropertyName(jContainer as JObject), pastedJToken));
				else
					jContainer.Add(pastedJToken);

				//Update the array count and all the indexes of 
				//	the children
				if (jContainer is JArray)
					this.RefreshTreeViewItem(targetJToken);
			}
		}

		private void Paste_CanExecute(object sender, CanExecuteRoutedEventArgs e)
		{
			JToken targetJToken = e.Parameter as JToken;
			JToken pastedJToken = GetJTokenFromClipBoard();

			e.CanExecute = false;

			if (!this.IsReadOnly)
			{
				if (targetJToken != null && pastedJToken != null)
				{
					if (targetJToken is JProperty)
						targetJToken = ((JProperty)targetJToken).Value as JContainer;

					if (targetJToken is JContainer)
					{
						if (targetJToken is JObject)
							e.CanExecute = ((pastedJToken is JProperty && !targetJToken.IsExistingProperty(pastedJToken)) || pastedJToken is JObject || pastedJToken is JArray || pastedJToken is JValue);
						else if (targetJToken is JArray && pastedJToken is JArray)
							e.CanExecute = false;
						else
							e.CanExecute = pastedJToken.Type != JTokenType.Property;
					}
				}
			}
		}

		#endregion

		#region "       OkCommand     "

		public RoutedUICommand OkCommand
		{
			get { return (RoutedUICommand)GetValue(OkCommandProperty); }
			set { SetValue(OkCommandProperty, value); }
		}

		public static readonly DependencyProperty OkCommandProperty = DependencyProperty.Register("OkCommand", typeof(RoutedUICommand), typeof(JsonEditorControl), new PropertyMetadata(null));

		private void Ok_Executed(object sender, ExecutedRoutedEventArgs e)
		{
			if (this.EditedJToken != null)
			{
				this.EditedJToken.JToken.Replace(this.EditedJToken.CreateNewToken());

				this.EditValueVisibility = Visibility.Collapsed;
			}
		}

		private void Ok_CanExecute(object sender, CanExecuteRoutedEventArgs e)
		{
			if (this.EditedJToken != null && !this.IsReadOnly)
				e.CanExecute = this.EditedJToken.IsValid();
		}

		#endregion

		#region "       CancelCommand     "

		public RoutedUICommand CancelCommand
		{
			get { return (RoutedUICommand)GetValue(CancelCommandProperty); }
			set { SetValue(CancelCommandProperty, value); }
		}

		public static readonly DependencyProperty CancelCommandProperty = DependencyProperty.Register("CancelCommand", typeof(RoutedUICommand), typeof(JsonEditorControl), new PropertyMetadata(null));

		private void Cancel_Executed(object sender, ExecutedRoutedEventArgs e)
		{
			this.EditValueVisibility = Visibility.Collapsed;
		}

		private void Cancel_CanExecute(object sender, CanExecuteRoutedEventArgs e)
		{
			e.CanExecute = !this.IsReadOnly;
		}

		#endregion

		#region "       NewPropertyCommand     "

		public RoutedUICommand NewPropertyCommand
		{
			get { return (RoutedUICommand)GetValue(NewPropertyCommandProperty); }
			set { SetValue(NewPropertyCommandProperty, value); }
		}

		public static readonly DependencyProperty NewPropertyCommandProperty = DependencyProperty.Register("NewPropertyCommand", typeof(RoutedUICommand), typeof(JsonEditorControl), new PropertyMetadata(null));

		private void NewProperty_Executed(object sender, ExecutedRoutedEventArgs e)
		{
			JToken targetjToken = e.Parameter as JToken;
			JObject targetjObject = null;
			JProperty jProperty = null;
			string name = null;

			if (targetjToken.Type == JTokenType.Object)
				targetjObject = targetjToken as JObject;
			else if (targetjToken.Type == JTokenType.Property && ((JProperty)targetjToken).Value.Type == JTokenType.Object)
				targetjObject = ((JProperty)targetjToken).Value as JObject;

			if (targetjObject != null)
			{
				name = this.GetNewPropertyName(targetjObject);

				jProperty = new JProperty(name, "");

				targetjObject.Add(jProperty);

				this.EditToken(jProperty);
			}
		}

		private void NewProperty_CanExecute(object sender, CanExecuteRoutedEventArgs e)
		{
			JToken targetjToken = e.Parameter as JToken;

			if (!this.IsReadOnly)
				e.CanExecute = targetjToken != null && (targetjToken.Type == JTokenType.Object || (targetjToken.Type == JTokenType.Property && ((JProperty)targetjToken).Value.Type == JTokenType.Object));
		}

		#endregion

		#region "       NewObjectCommand     "

		public RoutedUICommand NewObjectCommand
		{
			get { return (RoutedUICommand)GetValue(NewObjectCommandProperty); }
			set { SetValue(NewObjectCommandProperty, value); }
		}

		public static readonly DependencyProperty NewObjectCommandProperty = DependencyProperty.Register("NewObjectCommand", typeof(RoutedUICommand), typeof(JsonEditorControl), new PropertyMetadata(null));

		private void NewObject_Executed(object sender, ExecutedRoutedEventArgs e)
		{
			JToken targetjToken = e.Parameter as JToken;
			JArray targetjArray = null;

			if (targetjToken.Type == JTokenType.Array)
				targetjArray = targetjToken as JArray;
			else if (targetjToken.Type == JTokenType.Property && ((JProperty)targetjToken).Value.Type == JTokenType.Array)
				targetjArray = ((JProperty)targetjToken).Value as JArray;

			if (targetjArray != null)
				targetjArray.Add(new JObject());
		}

		private void NewObject_CanExecute(object sender, CanExecuteRoutedEventArgs e)
		{
			JToken targetjToken = e.Parameter as JToken;

			if (!this.IsReadOnly)
				e.CanExecute = targetjToken != null && (targetjToken.Type == JTokenType.Array || (targetjToken.Type == JTokenType.Property && ((JProperty)targetjToken).Value.Type == JTokenType.Array));
		}

		#endregion

		#region "       NewValueCommand     "

		public RoutedUICommand NewValueCommand
		{
			get { return (RoutedUICommand)GetValue(NewValueCommandProperty); }
			set { SetValue(NewValueCommandProperty, value); }
		}

		public static readonly DependencyProperty NewValueCommandProperty = DependencyProperty.Register("NewValueCommand", typeof(RoutedUICommand), typeof(JsonEditorControl), new PropertyMetadata(null));

		private void NewValue_Executed(object sender, ExecutedRoutedEventArgs e)
		{
			JToken targetjToken = e.Parameter as JToken;
			JArray targetjArray = null;
			JValue newJValue = null;

			if (targetjToken.Type == JTokenType.Array)
				targetjArray = targetjToken as JArray;
			else if (targetjToken.Type == JTokenType.Property && ((JProperty)targetjToken).Value.Type == JTokenType.Array)
				targetjArray = ((JProperty)targetjToken).Value as JArray;

			if (targetjArray != null)
			{
				newJValue = new JValue(string.Empty);

				targetjArray.Add(newJValue);

				this.EditToken(newJValue);
			}
		}

		private void NewValue_CanExecute(object sender, CanExecuteRoutedEventArgs e)
		{
			JToken targetjToken = e.Parameter as JToken;

			if (!this.IsReadOnly)
				e.CanExecute = targetjToken != null && (targetjToken.Type == JTokenType.Array || (targetjToken.Type == JTokenType.Property && ((JProperty)targetjToken).Value.Type == JTokenType.Array));
		}

		#endregion

		#region "       NavigateToCommand     "

		public RoutedUICommand NavigateToCommand
		{
			get { return (RoutedUICommand)GetValue(NavigateToCommandProperty); }
			set { SetValue(NavigateToCommandProperty, value); }
		}

		public static readonly DependencyProperty NavigateToCommandProperty = DependencyProperty.Register("NavigateToCommand", typeof(RoutedUICommand), typeof(JsonEditorControl), new PropertyMetadata(null));

		private void NavigateTo_Executed(object sender, ExecutedRoutedEventArgs e)
		{
			this.ExpandTreeViewItem(RootTreeview, e.Parameter as JToken);
		}

		private void NavigateTo_CanExecute(object sender, CanExecuteRoutedEventArgs e)
		{
			JToken targetjToken = e.Parameter as JToken;

			e.CanExecute = targetjToken != null;
		}

		#endregion

		#region "       SearchCommand     "

		public RoutedUICommand SearchCommand
		{
			get { return (RoutedUICommand)GetValue(SearchCommandProperty); }
			set { SetValue(SearchCommandProperty, value); }
		}

		public static readonly DependencyProperty SearchCommandProperty = DependencyProperty.Register("SearchCommand", typeof(RoutedUICommand), typeof(JsonEditorControl), new PropertyMetadata(null));

		private void Search_Executed(object sender, ExecutedRoutedEventArgs e)
		{
			List<JToken> foundItems = new List<JToken>();

			this.SearchResultsVisbility = Visibility.Visible;
			this.IsSearching = true;

			this.LoadAllFoundItems(this.DataContext as JToken, e.Parameter.ToString(), foundItems);

			this.FoundItems = foundItems;
			this.IsSearching = false;
		}

		private void Search_CanExecute(object sender, CanExecuteRoutedEventArgs e)
		{
			e.CanExecute = this.DataContext != null;
		}

		#endregion

		#region "       ToggleSearchResultsCommand     "

		public RoutedUICommand ToggleSearchResultsCommand
		{
			get { return (RoutedUICommand)GetValue(ToggleSearchResultsCommandProperty); }
			set { SetValue(ToggleSearchResultsCommandProperty, value); }
		}

		public static readonly DependencyProperty ToggleSearchResultsCommandProperty = DependencyProperty.Register("ToggleSearchResultsCommand", typeof(RoutedUICommand), typeof(JsonEditorControl), new PropertyMetadata(null));

		private void ToggleSearchResults_Executed(object sender, ExecutedRoutedEventArgs e)
		{
			if (e.Parameter.ToString().ToLower() == Visibility.Visible.ToString().ToLower())
				this.SearchResultsVisbility = Visibility.Visible;
			else
				this.SearchResultsVisbility = Visibility.Collapsed;
		}

		private void ToggleSearchResults_CanExecute(object sender, CanExecuteRoutedEventArgs e)
		{
			e.CanExecute = true;
		}

		#endregion

		#region "       ViewUpdateCommand     "

		public RoutedUICommand ViewUpdateCommand
		{
			get { return (RoutedUICommand)GetValue(ViewUpdateCommandProperty); }
			set { SetValue(ViewUpdateCommandProperty, value); }
		}

		public static readonly DependencyProperty ViewUpdateCommandProperty = DependencyProperty.Register("ViewUpdateCommand", typeof(RoutedUICommand), typeof(JsonEditorControl), new PropertyMetadata(null));

		private void ViewUpdate_Executed(object sender, ExecutedRoutedEventArgs e)
		{
			Process.Start(new ProcessStartInfo("http://jsoneditor.codeplex.com/"));
		}

		private void ViewUpdate_CanExecute(object sender, CanExecuteRoutedEventArgs e)
		{
			e.CanExecute = true;
		}

		#endregion

		#endregion

		#region "       Methods     "

		private string GetNewPropertyName(JObject targetjObject)
		{
			int i = 0;
			string seed = "NewProperty";
			string name = seed;

			if (targetjObject != null)
			{
				while (targetjObject[name] != null)
				{
					i++;
					name = string.Format("{0}_{1}", seed, i);
				}
			}

			return name;
		}

		public void Clear()
		{
			//Not sure how this is used in IBaseInspector2
		}

		private JToken ToJToken(byte[] array)
		{
			string json = null;
			JToken jToken = null;

			try
			{
				json = Encoding.UTF8.GetString(array);
				jToken = JsonConvert.DeserializeObject(json) as JToken;
			}
			catch (Exception)
			{
				//Not Json. Do Nothing
			}

			return jToken;
		}

		private byte[] ToByteArray(JToken jtoken)
		{
			if (jtoken == null)
				return null;
			else
				return Encoding.UTF8.GetBytes(jtoken.ToString());
		}

		public void UnsetDirtyFlag()
		{
			this.OriginalBody = ToByteArray(this.DataContext as JToken);
		}

		public override void OnApplyTemplate()
		{
			base.OnApplyTemplate();

			DragThumb = this.FindName("PART_DragThumb") as Thumb;
			if (DragThumb != null)
				DragThumb.DragDelta += new DragDeltaEventHandler(DragThumb_DragDelta);

			DragTranslateTransform = this.FindName("PART_DragTranslateTransform") as TranslateTransform;

			SearchResultThumb = this.FindName("PART_SearchResultThumb") as Thumb;
			if (SearchResultThumb != null)
				SearchResultThumb.DragDelta += new DragDeltaEventHandler(SearchResultThumb_DragDelta);

			ResizePropertyEditorThumb = this.FindName("PART_ResizePropertyEditorThumb") as Thumb;
			if (ResizePropertyEditorThumb != null)
				ResizePropertyEditorThumb.DragDelta += new DragDeltaEventHandler(ResizePropertyEditorThumb_DragDelta);

			PropertyEditor = this.FindName("PART_PropertyEditor") as Border;
		}

		private void DragThumb_DragDelta(object sender, DragDeltaEventArgs e)
		{
			if (DragTranslateTransform != null)
			{
				DragTranslateTransform.X += e.HorizontalChange;
				DragTranslateTransform.Y += e.VerticalChange;
			}
		}

		private void SearchResultThumb_DragDelta(object sender, DragDeltaEventArgs e)
		{
			// height of the title bar
			double minHeight = 20;

			SearchResultHeight += e.VerticalChange * -1;

			// Min height
			if (SearchResultHeight < minHeight)
				SearchResultHeight = minHeight;
		}

		private void ResizePropertyEditorThumb_DragDelta(object sender, DragDeltaEventArgs e)
		{
			double maxWidth = this.ActualWidth - 10;
			double maxHeight = this.ActualHeight - 10;
			double minWidth = 300;
			double minHeight = 200;
			double width = PropertyEditor.Width;
			double height = PropertyEditor.Height;

			width += e.HorizontalChange;
			height += e.VerticalChange;

			if (width > maxWidth)
				width = maxWidth;

			if (width < minWidth)
				width = minWidth;

			if (height > maxHeight)
				height = maxHeight;

			if (height < minHeight)
				height = minHeight;

			PropertyEditor.Width = width;
			PropertyEditor.Height = height;
		}

		private JToken GetJTokenFromClipBoard()
		{
			JToken jToken = null;
			bool isHostJObject = false;
			Regex jPropertyRegEx = new Regex(@"^"".*"":\s.*", RegexOptions.IgnoreCase);

			try
			{
				if (Clipboard.ContainsText())
				{
					string json = Clipboard.GetText().Trim();

					if (jPropertyRegEx.IsMatch(json) && !(json.StartsWith("{") && json.EndsWith("}")))
					{
						json = "{" + json + "}";
						isHostJObject = true;
					}

					jToken = JToken.Parse(json);

					if (isHostJObject)
						jToken = jToken.First;
				}
			}
			catch (Exception)
			{
				//Do Nothing
			}

			return jToken;
		}

		private void EditToken(JToken jToken)
		{
			if (jToken != null)
			{
				this.EditedJToken = new EditedJToken(jToken);

				EditValueVisibility = Visibility.Visible;
			}
		}

		private void ExpandTreeViewItem(TreeView container, JToken item)
		{
			TreeViewItem lastTreeViewItem = null;
			IEnumerable<JToken> ancestors = null;
			JToken currentItem = item;
			EventHandler statusChangedHandler = null;

			if (container != null && item != null)
			{
				ancestors = item.GetJTokenAncestors();

				foreach (JToken ancestor in ancestors)
				{
					if (lastTreeViewItem == null)
						lastTreeViewItem = container.Items.Cast<TreeViewItem>().FirstOrDefault(i => i.DataContext == ancestor);
					else
						lastTreeViewItem = lastTreeViewItem.ItemContainerGenerator.ContainerFromItem(ancestor) as TreeViewItem;


					if (lastTreeViewItem == null)
						break;
					else
					{
						lastTreeViewItem.IsExpanded = true;
						lastTreeViewItem.BringIntoView();
						lastTreeViewItem.Focus();

						if (lastTreeViewItem.ItemContainerGenerator.Status != GeneratorStatus.ContainersGenerated)
						{
							statusChangedHandler = (sender, e) =>
							{
								ItemContainerGenerator generator = sender as ItemContainerGenerator;

								if (generator != null && generator.Status == GeneratorStatus.ContainersGenerated)
								{
									generator.StatusChanged -= statusChangedHandler;

									this.ExpandTreeViewItem(RootTreeview, item);
								}
							};

							lastTreeViewItem.ItemContainerGenerator.StatusChanged += statusChangedHandler;
						}
					}
				}
			}
		}

		private void LoadAllFoundItems(JToken jToken, string searchText, List<JToken> foundItems)
		{
			JContainer jContainer = jToken as JContainer;
			JProperty jProperty = jToken as JProperty;
			JValue jValue = jToken as JValue;

			if (jToken != null && !string.IsNullOrEmpty(searchText))
			{
				if (jProperty != null)
				{
					if (jProperty.Name.LowerCaseContains(searchText))
						foundItems.Add(jProperty);

					if (jProperty.Value != null)
					{
						if (jProperty.Value is JValue && jProperty.Value.ToString().LowerCaseContains(searchText))
							foundItems.Add(jProperty);

						if (jProperty.Value is JContainer)
							LoadAllFoundItems(jProperty.Value, searchText, foundItems);
					}
				}
				else if (jContainer != null)
				{
					foreach (JToken item in jContainer.Children())
					{
						LoadAllFoundItems(item, searchText, foundItems);
					}
				}
				else if (jValue != null)
				{
					if (jValue.Value != null && jValue.Value.ToString().LowerCaseContains(searchText))
						foundItems.Add(jValue);
				}
			}
		}

		private void RefreshTreeViewItem(JToken jToken)
		{
			JToken clone = jToken.DeepClone();

			jToken.Replace(clone);

			this.ExpandTreeViewItem(this.RootTreeview, clone);
		}

		private void CheckUpdateAvailable()
		{
			WebClient client = null;
			DownloadStringCompletedEventHandler completeHandler = null;
			MatchCollection collection = null;
			string expression = @"v\d\.\d(\.\d)?";

			client = new WebClient();

			completeHandler = new DownloadStringCompletedEventHandler((sender, e) =>
			{
				client.DownloadStringCompleted -= completeHandler;

				if (e.Error == null && !string.IsNullOrEmpty(e.Result))
				{
					try
					{
						XElement mostRecentRelease = XDocument.Parse(e.Result).Descendants("title")
							.Where(n => Regex.IsMatch(n.Value, expression))
							.FirstOrDefault();

						if (mostRecentRelease != null && mostRecentRelease.Value != null)
						{
							collection = Regex.Matches(mostRecentRelease.Value, expression, RegexOptions.Singleline);

							if (collection.Count != 0)
							{
								FileVersionInfo currentVersion = FileVersionInfo.GetVersionInfo(System.Reflection.Assembly.GetExecutingAssembly().Location);

								string versionText = collection[0].Value;

								if (versionText.Split(".".ToCharArray()).Length == 2)
									IsUpdateAvailable = (versionText != string.Format("v{0}.{1}", currentVersion.ProductMajorPart, currentVersion.ProductMinorPart));
								else
									IsUpdateAvailable = (versionText != string.Format("v{0}.{1}.{2}", currentVersion.ProductMajorPart, currentVersion.ProductMinorPart, currentVersion.ProductBuildPart));
							}
						}
					}
					catch (Exception) { /*Do Nothing*/ }
				}

				client.Dispose();
			});

			client.DownloadStringCompleted += completeHandler;
			client.DownloadStringAsync(new Uri("http://jsoneditor.codeplex.com/project/feeds/rss?ProjectRSSFeed=codeplex%3a%2f%2frelease%2fjsoneditor"));
		}

		#endregion
	}

	#region "        ValueType       "

	public enum ValueType
	{
		String,
		Number,
		Boolean,
		Object,
		Array
	}

	#endregion
}
